#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#define MAP_HEIGHT 25					
#define MAP_WIDTH 60
#define WALL "■"
#define SNAKE "■"
#define SNAKE_SIZE 100  // 蛇头加上蛇身的最大节数
#define FOOD "●"

#define D_UP 'w'
#define D_RIGHT 'd' 
#define D_DOWN 's'
#define D_LEFT 'a'

struct
{ 
	int x[SNAKE_SIZE];	// 蛇头和蛇身的横坐标
	int y[SNAKE_SIZE];  // 蛇头和蛇身的纵坐标
	int len; 			// 蛇长
	int speed; 			// 移动速度	
	int direction;		// 移动方向 
} snake;
struct
{
	int x;
	int y;
}food;
/*--------获取 [a, b) 范围的随机整数------*/
int randomIn(int a, int b){
    srand((unsigned int)time(NULL));
	return rand() % b + a;
}

/*--------------------移动光标--------------------- */
void gotoxy(int x,int y) 
{
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);  // 获取当前运行程序的窗口 

    COORD coord;									  // 获取光标 
   
    coord.X = x;									  // 设置坐标					
    coord.Y = y;	
    
    SetConsoleCursorPosition(handle,coord);			  // 设置指定控制台屏幕缓冲区中的光标位置。 
}	
/*------------------ 擦除画面 --------------------*/
void clear(int x, int y)
{
	gotoxy(x, y);
    printf("  ");
}
/* ----------- 绘制小蛇 -------------*/
void drawSnake(){
	for(int i = 0;i < snake.len; i++)
    {
        gotoxy(snake.x[i],snake.y[i]);
        printf(SNAKE);
    } 
}
/*----------移动小蛇 --------*/
void move(){
	clear(snake.x[snake.len - 1], snake.y[snake.len -1]);
    // 先移动蛇身
    for(int i = snake.len - 1; i > 0 ; i--){
    	snake.x[i] = snake.x[i-1];
        snake.y[i] = snake.y[i-1];
    }
    // 控制蛇头
	switch (snake.direction){
        case D_UP:
            snake.y[0]--;
            break;
        case D_DOWN:
            snake.y[0]++;
            break;
        case D_LEFT:
            snake.x[0] -= 2;
            break;
        case D_RIGHT:
            snake.x[0] += 2;
            break;   
    }
    drawSnake();
}
/*-------------------- 隐藏光标 -------------------- */ 
void hideCursor()
{
	CONSOLE_CURSOR_INFO cursor;    
	cursor.bVisible = FALSE;    
	cursor.dwSize = sizeof(cursor);    
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);    
	SetConsoleCursorInfo(handle, &cursor);
}


/*------------ 判断坐标是否在蛇头或蛇身上--------------*/
bool isInSnake(int x,int y){
	for(int i = 0; i < snake.len; i++)
    	if(snake.x[i] == x && snake.y[i] == y)
            return true;
    
    return false;
}

/*------------ 随机生成食物的位置 --------*/
void randomFoodPosition(){
    do {
    	food.x = randomIn(2, MAP_WIDTH - 4);		// 食物在水平方向上必须在围墙内
        
        food.y = randomIn(1, MAP_HEIGHT - 2);		// 食物在竖直方向上必须在围墙内
    
    } while(isInSnake(food.x, food.y) || food.x % 2 != 0);		    // 当坐标不合理时, 则重新生成
}


/* ---获取用户按键--- */
int keyDown()
{	int key = -1;
    if(_kbhit())
    {
    	fflush(stdin);		// 刷新控制台输入的缓冲区
    	key = _getch();	    // 读取键盘的按键
    }
    return key;
}

/*----------绘制食物----------*/
void drawFood(){
	gotoxy(food.x, food.y);
    printf(FOOD);
}
/*----------------绘制地图------------- */
void drawMap(){
    // 绘制左右边界
    for(int i=0;i<=MAP_HEIGHT;i++)		// 遍历指定的地图高度
    {
        gotoxy(0,i);					// 将光标移动到边界最左边的位置
        printf(WALL);
        gotoxy(MAP_WIDTH,i);			// 将光标移动到边界最右边的位置
        printf(WALL);
    }
	// 绘制上下边界
    for(int i=0;i<=MAP_WIDTH;i+=2) 		// 遍历指定的地图宽度
    {									// 由于 ■ 符号在水平方向上是占两个字符的， 所以地图也相应的隔两个位置
        gotoxy(i,0);					// 将光标移动到边界最上边的位置
        printf(WALL);
        gotoxy(i,MAP_HEIGHT);
        printf(WALL);					// 将光标移动到边界最下边的位置
    }
}
/*--------------  判断下个位置是否吃到食物 --------------*/
bool canEating(){
	return snake.x[0] == food.x && snake.y[0] == food.y;
}
/*---------------  小蛇吃到食物 ----------------*/
bool snakeEatenFood(){
    if(canEating()){
    	// 记录尾部
		int x = snake.x[snake.len-1];
		int y = snake.y[snake.len-1]; 
        // 蛇的身体 + 1
        snake.len ++;
        if(snake.len < SNAKE_SIZE) {		// 若蛇还未达到最大长度
			switch(snake.direction){
				case D_UP: y++; break;
				case D_DOWN: y++; break;
				case D_RIGHT: x+=2;	break;
				case D_LEFT: x-=2; break;
			}	
			snake.x[snake.len-1] = x;
			snake.y[snake.len-1] = y;
            randomFoodPosition();
            drawFood();
            return true;
        } else{
        	snake.len --;
		}
    }
    return false;
}

bool gameover(){
    for(int i = 1; i < snake.len; i++)			// 蛇头碰到蛇身
    	if(snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
                return true;
    if(snake.x[0] < 2 || snake.x[0] > MAP_WIDTH - 2) 	// 蛇头超出了左/右边界
        return true;
    if(snake.y[0] < 0 || snake.y[0] > MAP_HEIGHT - 1)	// 蛇头超出了上/下边界
        return true;
    return false;
}
/*---------- 初始化游戏信息 ------------*/
void init(){
	hideCursor();				// 隐藏光标 
	snake.len = 1;				// 默认只有一个蛇头 
    snake.x[0] = MAP_WIDTH / 2;
    snake.y[0] = MAP_HEIGHT / 2;
    snake.speed = 1;			
    snake.direction = D_RIGHT;	// 初始化贪吃蛇方向 向右 
    randomFoodPosition();		// 产生随机食物 
    					 
    drawMap();					// 绘制边界
    
    drawFood();					// 绘制食物
     
    drawSnake();				// 绘制蛇
    
}
int main(){
    init();
	while (!gameover()){
		gotoxy(MAP_WIDTH +5, 0);			// 显示得分 
		printf("得分: %d", snake.len - 1);
        // 判断按键
        int keydown = keyDown();
        if(keydown != -1)
        {
        	snake.direction = char(keydown);
        }
        snakeEatenFood();
		move();
        Sleep(100 / snake.speed);				// 延迟
    }
}